<template>
  <div class="render-time-line-com common-scrollbar" @scroll="handleScroll">
    <div class="com-icon" @click.stop>
      <RenderIcon :time-line-arr="timeLineArrCopy" :playing="playing"></RenderIcon>
    </div>
    <div class="com-time" @dragover.prevent @drop.prevent="handleDrop">
      <div ref="scrollXRef" class="com-drag common-scrollbar" @scroll="scrollXComDrag">
        <draggable
          ref="timeLineRef"
          v-model="timeLineArrCopy"
          :style="{ width: (1 / scaleRate) * 100 + '%' }"
          :disabled="playing"
          :move="handleMove"
          class="com-scroll-item"
          @end="handleDragEnd"
        >
          <transition-group class="com-scroll-transition">
            <component
              :is="item.name"
              v-for="(item, index) in timeLineArrCopy"
              :key="item.mark"
              class="time-line-item"
              :timeline-item="item"
              :class="{
                'border-outset': item.childrenElList.length,
                select: item.currentSelect !== -1,
              }"
            ></component>
          </transition-group>
        </draggable>
      </div>
      <div
        v-show="showAxis"
        class="com-axis"
        :class="{ moving: axisMoving }"
        :style="axisStyle"
        @mousedown="handleAxisMousedown"
      ></div>
    </div>
  </div>
</template>

<script>
import vtuber from './timeLineCom/vtuber.vue';
import bgMusic from './timeLineCom/bgMusic.vue';
import sticker from './timeLineCom/sticker.vue';
import signature from './timeLineCom/signature.vue';
import graphics from './timeLineCom/graphics.vue';
import videoType from './timeLineCom/videoType.vue';
import shape from './timeLineCom/shape.vue';
import action from './timeLineCom/action.vue';
import background3d from './timeLineCom/background3d.vue';
import camera from './timeLineCom/camera.vue';
import textDrive from './timeLineCom/textDrive.vue';
import audioDrive from './timeLineCom/audioDrive.vue';
import aiFinTemplate from './timeLineCom/aiFinTemplate.vue';
import chart from './timeLineCom/chart.vue';
import emotion from './timeLineCom/emotion.vue';
import draggable from 'vuedraggable';
import eventBus from '@veditor/eventBus';
import RenderIcon from './renderIcon.vue';
import { TIMELINE_CONFIG, Z_INDEX_OBJ } from '../js/enums';
import AxisLine from '@veditor/components/timeLine/js/AxisLine';
import { debounce } from 'lodash';
import {
  calcAllWidth,
  parseTimeLineArr,
  reductionTimeLineArr,
} from '@veditor/components/timeLine/js/common';
const BASE_INDEX = Object.keys(Z_INDEX_OBJ).length; // 基础层次
export default {
  name: 'RenderTimeLine',
  components: {
    vtuber,
    bgMusic,
    sticker,
    signature,
    graphics,
    videoType,
    shape,
    action,
    background3d,
    camera,
    textDrive,
    audioDrive,
    aiFinTemplate,
    chart,
    emotion,
    draggable,
    RenderIcon,
  },
  model: {
    prop: 'timeLineArr',
    event: 'update',
  },
  props: {
    timeLineArr: {
      required: true,
      type: Array,
    },
  },
  data() {
    return {
      left: 0, // 默认左侧按钮区域160
      timeLineArrCopy: [], // 真正用于渲染时间轴的数组
      currentLeft: 0, // 当前时间轴相距右侧的偏移
      allWidth: 0, // 定义时间轴的宽度
      showDragArea: false,
      axisHeight: 260, // 时间轴高度
      axisLine: null, // 时间轴轴线对象
      scaleRate: TIMELINE_CONFIG.defaultScaleRate, //默认的比例
      iconAreaWidth: TIMELINE_CONFIG.iconAreaWidth,
      playing: false, // 表示正在播放中
      axisMoving: false,
      axisStartClientX: 0,
      copyScaleRate: TIMELINE_CONFIG.defaultScaleRate,
    };
  },
  computed: {
    // 判断时间轴中是否有3d数字人轴
    disableClickTimeline() {
      let hasVtuber = false;
      for (let i = 0; i < this.timeLineArr.length; i++) {
        const item = this.timeLineArr[i];
        if ((item.name === 'vtuber' && item.type === 'real') || item.name === 'aiFinTemplate') {
          hasVtuber = true;
        }
      }
      return hasVtuber;
    },
    showAxis() {
      return !!this.timeLineArr.length;
    },
    axisStyle() {
      const showFlag = this.left >= 0 && this.left <= this.allWidth;
      return {
        left: `${this.left + this.iconAreaWidth}px`,
        height: `${this.axisHeight}px`,
        zIndex: showFlag ? Number('10') : -1,
        visibility: showFlag ? 'visible' : 'hidden',
      };
    },
  },
  watch: {
    timeLineArr: {
      handler() {
        this.timeLineArrCopy = parseTimeLineArr(this.timeLineArr); // 这里进行数据处理,遍历寻找子元素
      },
      immediate: true,
    },
    playing() {
      this.updatePlayingStatus();
    },
  },
  mounted() {
    const DELAY = 500;
    this.judgePlaying = debounce(this.judgePlaying.bind(this), DELAY);
    this.axisLine = new AxisLine({ duration: 60 }); // 这里创建一个时间轴对象
    this.init();
    document.addEventListener('mouseup', this.handleAxisMouseup);
    document.addEventListener('mousemove', this.handleAxisMousemove);
  },
  beforeDestroy() {
    document.removeEventListener('mouseup', this.handleAxisMouseup);
    document.removeEventListener('mousemove', this.handleAxisMousemove);
  },
  methods: {
    // 处理拖拽结束
    handleDragEnd(e) {
      const { timeLineArr, timeLineArrCopy } = this;
      const { oldDraggableIndex, newDraggableIndex } = e;
      const oldMark = timeLineArr[oldDraggableIndex].mark;
      const newMark = timeLineArr[newDraggableIndex].mark;
      if (oldMark === newMark) {
        return;
      }
      this.$emit('dragEnd', oldMark, newMark);
      this.handleRefreshZIndex(); // 这里重构一下所有元素的层次,从最大-->最小
      this.$emit('update', reductionTimeLineArr(timeLineArrCopy)); // 这里需要对数据进行解开
    },
    // 处理拖拽中:禁用部分组件的拖动
    handleMove(e) {
      return !e.relatedContext.element.disable.col && !e.draggedContext.element.disable.col;
    },
    // 处理播放操作
    handleOperate({ messageType, data }) {
      switch (messageType) {
        case 'play': {
          this.axisLine.play(data);
          break;
        }
        case 'pause': {
          this.axisLine.pause(data);
          break;
        }
        case 'replay': {
          this.axisLine.replay(data);
          break;
        }
        case 'updateSpeed': {
          this.axisLine.updateSpeed(data);
          break;
        }
        case 'timeupdate': {
          // 这里用来判断是否是播放中
          this.playing = true;
          this.judgePlaying(); // 放了一个500ms的定时器
          this.axisLine.timeUpdate(data);
          break;
        }
        case 'ended': {
          this.axisLine.ended(data);
          break;
        }
        default:
          break;
      }
    },
    // 处理点击切换时间轴
    clickAxisLine(e) {
      if (!this.disableClickTimeline) {
        const currentTime = this.axisLine.clickEl(e);
        this.$emit('updateTime', currentTime);
      }
    },
    // 处理在目标区域放下
    handleDrop(e) {
      const componentData = e.dataTransfer.getData('materialdata');
      if (componentData && componentData !== '{}') {
        this.$emit('dropEnd', e);
      }
    },
    // 用于初始化
    init() {
      this.initPage();
      const { width: allRealWidth } = this.$refs.timeLineRef.$el.getBoundingClientRect();
      const { allWidth } = this;
      Object.assign(this.axisLine, {
        realWidth: calcAllWidth(allRealWidth),
        allWidth: calcAllWidth(allWidth),
        viewWidth: allWidth,
      });
      this.axisLine.updateLeft = this.updateLeft; // 添加更新left的回调
      this.axisLine.updateScrollLeft = this.updateScrollLeft; // 添加更新left的回调
    },
    // 用于组件大小改变
    resize() {
      // 必须等页面更新后再更新数据
      this.$nextTick(() => {
        this.initPage();
        const { width: allRealWidth } = this.$refs.timeLineRef.$el.getBoundingClientRect();
        const { allWidth } = this;
        Object.assign(this.axisLine, {
          realWidth: calcAllWidth(allRealWidth),
          allWidth: calcAllWidth(allWidth),
          viewWidth: allWidth,
        });
        this.axisLine.resize();
      });
    },
    // 用于拖动位置后刷新zIndex
    handleRefreshZIndex() {
      const { timeLineArrCopy } = this;
      const LOCK_INDEX_ARR = Object.keys(Z_INDEX_OBJ);
      let baseZIndex = BASE_INDEX;
      for (let i = timeLineArrCopy.length - 1; i >= 0; i--) {
        const item = timeLineArrCopy[i];
        if (!LOCK_INDEX_ARR.find(k => k === item.name)) {
          item.zIndex = baseZIndex++; // 先赋值,再自+1
        }
      }
    },
    // 更新指针的时长
    updateDuration(videoDuration) {
      this.axisLine.updateDuration(videoDuration);
    },
    // 处理时间轴缩放比例变化
    scaleRateChange(scaleRate) {
      this.scaleRate = scaleRate;
    },
    // 处理x轴线滚动事件
    scrollXComDrag(e) {
      if (this.playing) {
        return;
      }
      eventBus.$emit('timeline:scroll');
      const { scrollLeft } = e.target;
      this.axisLine.scrollLeft = scrollLeft;
      this.axisLine.calcAPBS();
      this.$emit('scrollSync', scrollLeft);
    },
    // 用于判断是否直播中,直播中会一直执行,防抖事件会一直卡住,直到停止播放
    judgePlaying() {
      this.playing = false;
      this.axisLine.playing = false;
    },
    // 给每个组件更新播放状态
    updatePlayingStatus() {
      const { timeLineArr, playing } = this;
      if (timeLineArr.length) {
        timeLineArr.forEach(item => {
          item.updatePlaying(playing);
        });
      }
    },
    // 处理当前页面初始化操作
    initPage() {
      const { height } = document.querySelector('.time-line-com').getBoundingClientRect();
      const { width } = this.$refs.scrollXRef.getBoundingClientRect();
      this.axisHeight = height;
      this.allWidth = width;
      // 添加子函数控制滚动的函数
      this.timeLineArr.forEach(item => {
        item.changeScroll = this.scrollXComDrag;
      });
    },
    // 处理轴线对象更新left值
    updateLeft(left) {
      this.left = left;
    },
    updateScrollLeft(scrollLeft) {
      this.$refs.scrollXRef.scrollLeft = scrollLeft;
      this.$emit('scrollSync', scrollLeft);
    },
    handleScroll() {
      eventBus.$emit('timeline:scroll');
    },
    // 轴线鼠标按下
    handleAxisMousedown() {
      this.axisMoving = true;
      const { left } = this.$refs.scrollXRef.getBoundingClientRect();
      this.axisStartClientX = left;
    },
    // 轴线鼠标抬起
    handleAxisMouseup() {
      this.axisMoving = false;
    },
    // 轴线鼠标移动
    handleAxisMousemove(e) {
      if (!this.axisMoving) {
        this.handleAxisMouseup();
        return false;
      }
      const newLeft = e.clientX - this.axisStartClientX;
      const currentTime = this.axisLine.moveEl(newLeft);
      this.$emit('updateTime', currentTime);
    },
  },
};
</script>

<style lang="stylus" scoped>
.render-time-line-com
  padding 4px 8px 12px
  overflow-y scroll
  scrollbar-gutter 4px
  height 190px
  display flex
  margin-right unset
  flex 1
  &::-webkit-scrollbar-track
    background transparent
  .com-icon
    height 100%
  .com-time
    flex 1
    height 100%
    overflow-y visible
    width 1px
    .com-drag
      overflow-x scroll
      min-height 100%
      padding-bottom 8px
      margin-right unset
      &::-webkit-scrollbar-track
        background transparent
    .com-axis
      position absolute
      top 18px
      left 0
      height 100%
      width 2px
      background-color #FFFFFF
      transition all 60ms linear
      z-index 10
      cursor ew-resize
      &::before
        content ''
        position absolute
        top -17px
        left -4px
        background transparent
        width 10px
        height 14px
        border-left 3px solid #fff
        border-top 3px solid #fff
        border-right 3px solid #fff
        border-top-left-radius 3px
        border-top-right-radius 3px
      &::after
        content ''
        position absolute
        top -6px
        left -2.4px
        width 6.8px
        height 6.8px
        background transparent
        border-left 3px solid #fff
        border-top 3px solid #ffff
        transform rotate(-135deg)
        border-top-left-radius 3px
      &.moving
        transition unset
        &::before
        &::after
          background #FFFFFF
// 选中的样式
.chosen-style
  //width:1000px;
// 目标样式
.ghost-style
  //background-color:transparent;
.com-scroll-transition
  display flex
  flex-direction column
</style>
<style lang="stylus">
.time-line-item
  margin-bottom 6px
  &.border-outset
    // 这里主要是对于多元素的轴显示一个透明边框,多元素的轴不显示里边框
    border 2px solid transparent
    border-radius 4px
    &.select
      border 2px solid #979797
  .line-item
    .show-padding
      padding 0 20px
      height 100%
    .content-preview
      display flex
      align-items center
      min-width 140px
      .content-img
        width 22px
        height 22px
        border-radius 2px
        background rgba(255, 255, 255, 0.3)
        margin-right 8px
        img
          width 100%
          height 100%
          vertical-align top
          object-fit contain
      .content-duration
        margin-left 8px
      .content-text
        flex 1
        white-space nowrap
        overflow hidden
        text-overflow ellipsis
    .image-position
      background-repeat repeat no-repeat
      background-size contain
      height 100%
</style>
